#include<iostream>
using namespace std;
#include "gate.h"

void gate::allocate( int ro, int co ){
    m = new complex<type> * [ro];
    for ( int r = 0; r < ro; ++r ){
        m[r] = new complex<type>[co];
    }
}

void gate::initialize( complex<type> ** am ){   //copy
    for ( int r = 0; r < ro; ++r ){
        for ( int c = 0; c < co; ++c ){
            m[r][c] = am[r][c];
        }
    }
}

void gate::delete_gate(){
    if ( m ){
        for ( int r = 0; r < ro; ++r ){ delete [] m[r]; }
        delete [] m;
    }
}

gate::gate(int r, int c, int typ){   //constructor
    ro = r; co = c;
    allocate( ro, co );
    switch ( typ ){
        case I:
            if ( ro != co ){
                cout << "row != col" << endl;
                return;
            }
            for ( int i = 0; i < ro; ++ i )
                m[i][i] = complex<type>{1};
            break;
        case H:
            if ( ro != 2 || co != 2 ){
                cout << "invalid hadmard gate" << endl;
                return;
            }
            m[0][0] = complex<type>{1.0 / sqrt(2)};
            m[0][1] = complex<type>{1.0 / sqrt(2)};
            m[1][0] = complex<type>{1.0 / sqrt(2)};
            m[1][1] = complex<type>{-1.0 / sqrt(2)};
        default:
            break;
        }
}

gate::gate( const gate & a ):ro(a.ro),co(a.co){   //copy constructor
    // cout << "COPY" << endl;
    allocate( ro, co ); //allocate memory for this->gate
    initialize( a.m );  //copy
}

gate::gate( gate && a):ro(a.ro),co(a.co){    //move constructor
    // cout << "MOVE COPY" << endl;
    m = a.m;    //change the address of this->gate
    a.ro = 0; a.co = 0; a.m = NULL; //set null
}

gate & gate::operator=( const gate & a ){//copy assignment
    // cout << "COPY ASSIGN" << endl;
    if ( this != &a ){
        delete_gate();    //delete the original gate
        ro = a.ro; co = a.co;
        allocate( ro, co ); //reallocate
        initialize( a.m );  //copy
    }
    return *this;
}

gate & gate::operator=( gate && a ){  //move assignment
    // cout << "MOVE ASSIGN" << endl;
    if ( this != &a ){
        delete_gate();
        ro = a.ro; co = a.co;
        m = a.m;    //change the address of this->gate
        a.ro = 0; a.co = 0; a.m = NULL; //set null
    }
    return *this;
}

complex<type> & gate::operator()(int i, int j){
    if ( i < 0 || i >= ro || j < 0 || j >= co ) throw gate_exception();//out of range
    return m[i][j];
}

gate gate::operator+( const gate & a ){
    cout << "PLUS" << endl;
    if ( ro!=a.ro || co!=a.co ) throw gate_exception(); //different shape
    gate res{a};  //the copy constructor is called instead of move constructor in the given example
    for ( int i = 0; i < ro; ++i ){
        for ( int j = 0; j < co; ++j ){
            res.m[i][j] += m[i][j];
        }
    }
    return res;
}

int gate::row()const{ return ro; }

int gate::col()const{ return co; }

ostream & operator<<( ostream & o, const gate & a ){  //output the gate
    int i{0},j{0};
    for ( ; i < a.ro; ++i ){
        for ( j = 0; j < a.co - 1 ; ++j ){
            o << a.m[i][j] << ' ';
        }
        o << a.m[i][j] << endl;
    }
    return o;
}

gate::~gate(){  //destructor
    delete_gate();
}

// 张量积
gate tensorProduct(gate & a, gate & b){
    // printf("a.ro = %d a.co = %d b.ro = %d b.co = %d\n", a.ro, a.co, b.ro, b.co);
    gate g(a.ro * b.ro, a.co * b.co);
    // printf("g.ro = %d g.co = %d\n", g.ro, g.co);
    for ( int i = 0; i < g.ro; ++ i ){
        for ( int j = 0; j < g.co; ++ j ){
            g.m[i][j] = a.m[i/b.ro][j/b.co] * b.m[i%b.ro][j%b.co];
        }
    }
    return g;
}